#include <iostream>
#include <thread>
#include <chrono>
#include <vector>
#include <mutex>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <inttypes.h>
#include <csignal>
#include <sys/time.h>
#include <pthread.h>

#if defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#include <io.h>
#endif


#include "xorcrypto.h"

#include "log.h"




static volatile bool gs_bRunningStatus = true;


static void onSigInt(int signum)
{
    signal(signum, SIG_DFL); // 还原默认的信号处理
    gs_bRunningStatus = false;
    //printf("Runing status: %d.\r\n", gs_bRunningStatus);
    return;
}

/* select 精确睡眠,单位ms */
int select_sleep(int ms_time)
{
#ifdef __linux__
    struct timeval timeout = {0, ms_time * 1000};   //{秒, 微秒}
    select(0, nullptr, nullptr, nullptr, &timeout);
#else
    Sleep(mill_sec);
#endif

    return 0;
}

static std::string getCurFormatTimestamp()
{
    std::string strTime = "";

#if 0
    time_t tloc;
    struct tm tm;
    struct timeval tv;

    time(&tloc);
    localtime_r(&tloc, &tm);
    gettimeofday(&tv, nullptr);

    char szFormatTime[32] = {0};
    snprintf(szFormatTime, sizeof(szFormatTime), "%04d-%02d-%02d %02d:%02d:%02d.%03d",
             tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
             tm.tm_hour, tm.tm_min, tm.tm_sec, static_cast<int>(tv.tv_usec) / 1000);
    strTime = std::string(szFormatTime);
#else
    time_t tloc;
    struct tm tm;
    struct timeval tv;

    time(&tloc);
    gettimeofday(&tv, nullptr);

    char szStrTime[32] = {0};
    strftime(szStrTime, sizeof (szStrTime), "%Y-%m-%d %H:%M:%S", localtime_r(&tloc, &tm)); // safety thread

    char szTime[32] = {0};
    snprintf(szTime, sizeof (szTime), "%s.%03d", szStrTime, static_cast<int>(tv.tv_usec / 1000));
    strTime = std::string(szTime);
#endif

    return strTime;
}
#if 0
static unsigned int uabs(int value)
{
    unsigned int copyed_value = static_cast<unsigned int>(value);
    return (copyed_value > 0x80000000) ? static_cast<unsigned int>(-value) : copyed_value;
}


static void pfnHeartBeatTimerTodo()
{
    auto start = std::chrono::system_clock::now();
    while (gs_bRunningStatus)
    {
        std::this_thread::sleep_for(std::chrono::seconds(60));
        auto current = std::chrono::system_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(current - start);

        auto span = double(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den;
        if (span > (3 * 60))
        {
            start = std::chrono::system_clock::now();
            printf("timestamp [%s] ++++++++program is alive...\r\n", getCurFormatTimestamp().data());
        }
    }

    printf("timestamp [%s] ++++++++program is alive...\r\n", getCurFormatTimestamp().data());

    return;
}
#endif

void testMalloc()
{
    char szTestData[32] = "hello, this is test data.";
#if 0
    size_t s_dataSize = 10 * 1024 * 1024;

    char* pTestData = nullptr;
    pTestData = (char *) malloc(s_dataSize);

    if (pTestData) {
        memset(pTestData, 0, s_dataSize);
        size_t s_step = 1024;
        for (size_t s_index = strlen(szTestData); s_index < s_dataSize; s_index += s_step) {
            memcpy(pTestData + (s_index - strlen(szTestData)), szTestData, strlen(szTestData));
            printf("timestamp [%s], copy info: %s.\r\n", getCurFormatTimestamp().data(), pTestData + (s_index - strlen(szTestData)));
            std::this_thread::sleep_for(std::chrono::milliseconds(500));

            if (!gs_bRunningStatus) {
                printf("break out for loop.\r\n");
                break;
            }
        }
    }

    if (pTestData) {
        free(pTestData);
        pTestData = nullptr;
    }
#endif

    size_t szDataLen[8] = {1 * 1024 * 1024
                           , 5 * 1024 * 1024
                           , 10 * 1024 * 1024
                           , 20 * 1024 * 1024
                           , 30 * 1024 * 1024
                           , 40 * 1024 * 1024
                           , 50 * 1024 * 1024
                           , 60 * 1024 * 1024};

    size_t s_index = 0;
    do {
        std::string strData("");

        //strData.reserve(szDataLen[s_index]);
        strData.resize(szDataLen[s_index]);
        memcpy((char *)strData.data() + (strData.size() - strlen(szTestData)), szTestData, strlen(szTestData));
        printf("timestamp [%s], buf size: %u, copy info: %s.\r\n", getCurFormatTimestamp().data(), szDataLen[s_index], (char *)strData.data() + (strData.size() - strlen(szTestData)));

        ++s_index;
        s_index = (8 <= s_index) ? 0 : s_index;
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    } while (gs_bRunningStatus);

    printf("break out while loop.\r\n");

    return;
}

void testNew()
{
    char szTestData[32] = "hello, this is test data.";
    size_t s_dataSize = 10 * 1024 * 1024;

    char* pTestData = nullptr;
    pTestData = new char[s_dataSize];

    if (pTestData) {
        memset(pTestData, 0, s_dataSize);
        size_t s_step = 1024;
        for (size_t s_index = strlen(szTestData); s_index < s_dataSize; s_index += s_step) {
            memcpy(pTestData + (s_index - strlen(szTestData)), szTestData, strlen(szTestData));
            printf("timestamp [%s], copy info: %s.\r\n", getCurFormatTimestamp().data(), pTestData + (s_index - strlen(szTestData)));
            std::this_thread::sleep_for(std::chrono::milliseconds(500));

            if (!gs_bRunningStatus) {
                printf("break out for loop.\r\n");
                break;
            }
        }
    }

    if (pTestData) {
        delete [] pTestData;
        pTestData = nullptr;
    }

    return;
}

void printHelp(const char *pInfo)
{
    if (pInfo)
    {
        std::cout << "Error: Incorrect arguments." << std::endl;
        std::cout << "Usage: " << std::endl << std::endl;
        std::cout << "Your input is: " <<pInfo << ", but m for malloc, n for new." << std::endl << std::endl;
    }

    return;
}

void testSpace(int argc, char** argv)
{
    int type = 0;

    if (argc >= 2) {
        if (0 == strcmp(argv[1], "m")) {
            type = 1;
        } else if (0 == strcmp(argv[1], "n")) {
            type = 2;
        } else {
            type = 0;
        }
    }

    if (0 == type) {
        printHelp(argv[1]);
        return;
    }

    std::cout << "Starting, running in mode: " << type << std::endl;

    switch (type) {
    case 1: {
        testMalloc();
        break;
    } case 2: {
        testNew();
        break;
    } default: {
        break;
    }
    }

    return;
}

static void pfnThreadFunction()
{
    volatile size_t index = 0;
    while (gs_bRunningStatus) {
        std::cout << getCurFormatTimestamp() << " deal data in sub thread function." << std::endl;
        LOG_INFO("deal data in sub thread function, func: %d.", ++index);
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    return;
}

void* thread_func(void* arg)
{
    pthread_attr_t stThreadAttr;
    pthread_attr_init(&stThreadAttr);
    size_t sStackSize = 0;
    pthread_attr_getstacksize(&stThreadAttr, &sStackSize);
    std::cout << "Thread running with custom stack size: " << sStackSize << std::endl;

    volatile size_t index = 0;
    while (gs_bRunningStatus) {
        std::cout << getCurFormatTimestamp() << " deal data in sub thread function." << std::endl;
        LOG_INFO("deal data in sub thread function, func: %d.", ++index);
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    return nullptr;
}

void testLog()
{
    //thread_func(nullptr);
#if 1
    pthread_attr_t stThreadAttr;
    pthread_attr_init(&stThreadAttr);
    size_t sStackSize = 0;
    pthread_attr_getstacksize(&stThreadAttr, &sStackSize);
    std::cout << "thread stack defualut size: " << sStackSize << std::endl;

    //sStackSize = 2 * 1024;
    //pthread_attr_setstacksize(&stThreadAttr, sStackSize);

    //std::size_t sStackSize = 1024 * 1024;
    //std::thread thFunc(std::thread(pfnThreadFunction), std::move(sStackSize));
    //std::thread thFunc(pfnThreadFunction);
    //pthread_attr_t stThreadAttr;
    //pthread_getattr_np(thFunc.native_handle(), &stThreadAttr);
    //size_t sStackSize = 2 * 1024 * 1024;
    //pthread_attr_setstacksize(&stThreadAttr, sStackSize);
    //pthread_setattr_default_np(&stThreadAttr);
    //thFunc.join();

    pthread_t thread;
    pthread_attr_t attr;
    size_t stack_size = 1 * 1024 * 1024; // 设置堆栈大小为1MB

    pthread_attr_init(&attr);
    pthread_attr_setstacksize(&attr, stack_size);
    pthread_create(&thread, &attr, thread_func, nullptr);
    pthread_join(thread, nullptr);

    pthread_attr_destroy(&attr);
#endif

#if 0
    volatile size_t index = 0;

    do {
#if 0
        std::thread thLambdaFunc([](int loop){
            std::cout << getCurFormatTimestamp() << " deal data in thread 0 function." << std::endl;
            LOG_INFO("deal data in thread 0 function, func: %d.", loop);
            //std::this_thread::sleep_for(std::chrono::seconds(3));
        }, ++index);
        thLambdaFunc.detach();
        //thLambdaFunc.join();

        std::thread thLambdaFunc0([](int loop){
            std::cout << getCurFormatTimestamp() << " deal data in thread 1 function." << std::endl;
            LOG_INFO("deal data in thread 1 function, func: %d.", loop);
            //std::this_thread::sleep_for(std::chrono::seconds(3));
        }, ++index);
        thLambdaFunc0.detach();
        //thLambdaFunc0.join();
#endif
        //std::cout << getCurFormatTimestamp() << " deal data in main thread function." << std::endl;
        //LOG_INFO("deal data in main thread function, func: %d.", ++index);

        std::this_thread::sleep_for(std::chrono::milliseconds(50));
        //std::this_thread::sleep_for(std::chrono::seconds(1));

    } while (gs_bRunningStatus);
#endif

    //pthread_attr_destroy(&stThreadAttr);

    return;
}

void* threadFunction(void* arg) {
    pthread_attr_t* attr = static_cast<pthread_attr_t*>(arg);
    size_t stack_size = 0;
    pthread_attr_getstacksize(attr, &stack_size);
    std::cout << "sub thread stack size: " << stack_size << " byte" << std::endl;

    //volatile size_t index = 0;
    size_t index = 0;
    while (gs_bRunningStatus) {
        std::cout << getCurFormatTimestamp() << " deal data in sub thread function." << std::endl;
        //LOG_INFO("deal data in sub thread function, func: %d.", ++index);
        //std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        select_sleep(1000);
    }

    return nullptr;
}

void testThread()
{
    //std::vector<std::thread> threads;
    std::vector<pthread_t> threads;

    // 创建线程
    for (int i = 0; i < 1; ++i) {
        pthread_t thread;
        pthread_attr_t attr;
        size_t stack_size = 128 * 1024;

        // 初始化线程属性
        pthread_attr_init(&attr);

        pthread_attr_getstacksize(&attr, &stack_size);
        std::cout << "thread default stack size: " << stack_size << " byte" << std::endl;

        // 设置栈大小为16KB，设置的栈大小不能小于系统允许的最小值（通常为16KB）
        pthread_attr_setstacksize(&attr, 16 * 1024);

        pthread_attr_getstacksize(&attr, &stack_size);
        std::cout << "thread modify stack size: " << stack_size << " byte" << std::endl;

        // 创建线程
        pthread_create(&thread, &attr, threadFunction, &attr/*nullptr*/);

        // 销毁线程属性
        pthread_attr_destroy(&attr);

        // 将线程句柄存储到vector中
        //threads.push_back(std::thread(threadFunction));
        threads.emplace_back(thread);
    }

    // 等待线程结束
    for (auto& t : threads) {
        //t.join();
        pthread_join(t, nullptr);
    }

    return;
}

void testCrypto()
{
    std::string strContent = "threshold1=0\r\nlanguage=45\r\nlocation=nanjing";
    char szEnData[128] = {0};
    char szDeData[128] = {0};

    XORCryptoByDefaultKey(strContent.data(), strContent.length()
                          , szEnData, sizeof (szEnData));

    printf("default encrypt ret: %s.\r\n", szEnData);

    XORCryptoByDefaultKey(szEnData, strlen(szEnData)
                          , szDeData, sizeof (szDeData));

    printf("default decrypt ret: %s.\r\n", szDeData);

    std::string strTest = "this is test info!!!";
    std::string strKey = "key@@@123";

    memset(szEnData, 0, sizeof (szEnData));
    memset(szDeData, 0, sizeof (szDeData));

    XORCrypto(strTest.data(), strTest.length()
              , strKey.data(), strKey.length()
              , szEnData, sizeof (szEnData));

    printf("key encrypt ret: %s.\r\n", szEnData);

    XORCrypto(szEnData, strlen(szEnData)
              , strKey.data(), strKey.length()
              , szDeData, sizeof (szDeData));

    printf("key decrypt ret: %s.\r\n", szDeData);

    return;
}

int main(int argc, char** argv)
{
    signal(SIGINT, onSigInt);

    //testSpace(argc, argv);
    //testLog();
    //testThread();
    testCrypto();

#if 0
    while (gs_bRunningStatus)
    {
        //std::cout << "Running..." << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
#endif

    std::cout << "Stoping..." << std::endl;

    getchar();
    return 0;
}
